package com.chen.authority.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chen.authority.common.constant.SysConstant;
import com.chen.authority.common.model.PageQo;
import com.chen.authority.common.response.Response;
import com.chen.authority.common.util.WrapperUtil;
import com.chen.authority.mapper.MenuMapper;
import com.chen.authority.mapper.RoleMenuMapper;
import com.chen.authority.mapper.UserRoleMapper;
import com.chen.authority.model.dto.RoleDto;
import com.chen.authority.model.po.MenuPo;
import com.chen.authority.model.po.RoleMenuPo;
import com.chen.authority.model.po.RolePo;
import com.chen.authority.model.po.UserRolePo;
import com.chen.authority.model.qo.RoleQo;
import com.chen.authority.model.vo.RoleVo;
import com.chen.authority.service.RoleService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import com.chen.authority.mapper.RoleMapper;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 角色Service业务层处理
 *
 * @author chengy
 * @date 2022-08-17
 */
@Service
@RequiredArgsConstructor
public class RoleServiceImpl extends ServiceImpl<RoleMapper, RolePo> implements RoleService {

    private final MenuMapper menuMapper;
    private final UserRoleMapper userRoleMapper;
    private final RoleMenuMapper roleMenuMapper;
    private final RedisTemplate<String, Object> redisTemplate;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response addOrUpdateRole(Integer id, RoleDto dto) {
        boolean exists = baseMapper.exists(Wrappers
                .<RolePo>lambdaQuery()
                .and(sql -> sql
                        .eq(RolePo::getName, dto.getName())
                        .or()
                        .eq(RolePo::getCode, dto.getCode()))
                .ne(ObjectUtil.isNotNull(id), RolePo::getId, id));
        if (exists) return Response.fail("角色名称或编码重复");

        RolePo role = dto.toPo(RolePo.class);

        if (ObjectUtil.isNotNull(id)) {
            if (dto.getStatus() == SysConstant.DISABLE && checkRoleIsUse(id)) {
                return Response.fail("该角色被用户所关联，无法禁用");
            }
            roleMenuMapper.delete(Wrappers.<RoleMenuPo>lambdaQuery().eq(RoleMenuPo::getRoleId, id));
            role.setId(id);
        }

        this.saveOrUpdate(role);

        List<Integer> menuIds = dto.getMenuIds();
        if (CollUtil.isNotEmpty(menuIds)) {
            List<RoleMenuPo> roleMenus = new ArrayList<>();
            dto.getMenuIds().forEach(menuId -> roleMenus.add(new RoleMenuPo(role.getId(), menuId)));
            roleMenuMapper.insertBatch(roleMenus);
        }

        redisTemplate.delete(redisTemplate.keys(SysConstant.MENU_KEY + "*"));

        return Response.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response deleteRoleByIds(Integer[] roleIds) {
        if (checkRoleIsUse(roleIds)) {
            return Response.fail("存在被用户关联的角色，无法删除");
        }
        baseMapper.deleteBatchIds(Arrays.asList(roleIds));
        roleMenuMapper.delete(Wrappers.<RoleMenuPo>lambdaQuery().in(RoleMenuPo::getRoleId, roleIds));
        return Response.success();
    }

    @Override
    public Page<RoleVo> pageQueryRoleList(PageQo pageQo, RoleQo roleQo) {
        return baseMapper.selectVoPage(pageQo.build(), WrapperUtil.buildQueryWrapper(roleQo));
    }

    @Override
    public List<RoleVo> exportRoleList(QueryWrapper<RolePo> queryWrapper) {
        return baseMapper.selectVoList(queryWrapper);
    }

    @Override
    public List<RoleVo> findRolesByUserId(Object userId) {
        List<Integer> roleIds = userRoleMapper.selectList(Wrappers
                .<UserRolePo>lambdaQuery()
                .eq(UserRolePo::getUserId, userId))
                .stream()
                .map(UserRolePo::getRoleId)
                .collect(Collectors.toList());
        return CollUtil.isNotEmpty(roleIds) ? baseMapper.selectVoBatchIds(roleIds) : null;
    }

    @Override
    public List<Integer> findMenuIdsByRoleId(Integer roleId) {
        List<Integer> data = new ArrayList<>();
        List<Integer> menuIds = roleMenuMapper.selectList(Wrappers
                .<RoleMenuPo>lambdaQuery()
                .eq(RoleMenuPo::getRoleId, roleId))
                .stream()
                .map(RoleMenuPo::getMenuId)
                .collect(Collectors.toList());
        if (CollUtil.isNotEmpty(menuIds)) {
            List<MenuPo> menus = menuMapper.selectBatchIds(menuIds);
            menus.forEach(menu -> {
                boolean isMatch = menus.stream()
                        .noneMatch(item -> item.getParentId().equals(menu.getId()));
                if (isMatch) data.add(menu.getId());
            });
        }
        return data;
    }

    private boolean checkRoleIsUse(Integer... ids) {
        return userRoleMapper.exists(Wrappers.<UserRolePo>lambdaQuery().in(UserRolePo::getRoleId, ids));
    }

}
